package com.virtualightning.core.processor.observer.processor

import com.squareup.kotlinpoet.*
import com.virtualightning.anno.processor.BaseProcessor
import com.virtualightning.core.processor.observer.anno.IObserverConvertor
import com.virtualightning.core.processor.observer.anno.ObserverBeanGenerator
import com.virtualightning.core.processor.observer.anno.ObserverKey
import java.util.*
import java.util.concurrent.Future
import java.util.concurrent.FutureTask
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement

/**
 * 观察对象数据模型生成器
 */
class ObserverBeanGeneratorBinder: BaseProcessor() {
	companion object {
		const val OBSERVER_CLASS_NAME = "com.virtualightning.mvvm.binding.ObserverLiveData"
		const val PKG_NAME = "com.virtualightning.mvvm.binding"
	}

	/**
	 * 支持的注解类型
	 * [ObserverBeanGenerator] 绑定 ViewHolder 事件
	 * @return MutableSet<String>
	 */
	override fun getSupportedAnnotationTypes(): MutableSet<String> {
		return mutableSetOf(
			ObserverBeanGenerator::class.java.canonicalName
		)
	}

	/**
	 * 处理注解回调方法
	 * @param elements MutableSet<out TypeElement>
	 * @return Boolean
	 */
	override fun onProcess(elements: MutableSet<out TypeElement>): Boolean {
		val firstStepFutureList = LinkedList<Future<Unit>>()
		for (element in roundEnv.getElementsAnnotatedWith(ObserverBeanGenerator::class.java)) {
			val future = handleElementStep(element)
			exec(future)
			firstStepFutureList.add(future)
		}

		firstStepFutureList.forEach {
			it.get()
		}

		return true
	}

	/**
	 * 异步处理注解
	 * @param element Element 元素
	 */
	private fun handleElementStep(element: Element): FutureTask<Unit> {
		return FutureTask {
			val poolClassName = ClassName(PKG_NAME, "SubjectPool")

			if(!checkClass(element)) {
				logError("ObserverBeanGenerator 只能应用于 data class 类: ${element.javaClass}")
			}

			val typeElement = element as TypeElement
			val typeAnno = typeElement.getAnnotation(ObserverBeanGenerator::class.java)
			val initMethods = typeAnno.initMethod

			val rawClassName: ClassName = typeElement.asClassName()
			val rawClassPackage: String = rawClassName.packageName()
			val rawClassNameStr: String = rawClassName.simpleName()

			val fieldList = typeElement.enclosedElements.mapNotNull {
				if(checkField(it)) {
					if(it.getAnnotation(Transient::class.java) != null) {
						// 忽略 Transient 注解的属性
						null
					} else {
						it as VariableElement
					}
				} else {
					null
				}
			}.toList()



			if(fieldList.isEmpty()) {
				return@FutureTask
			}



			val beanName = if(rawClassNameStr.endsWith("Observer")) {
				rawClassNameStr.replace("Observer", "Bean")
			} else {
				"${rawClassNameStr}Bean"
			}
			val beanClassName = ClassName(rawClassPackage, beanName)
			// 生成类构造器
			val typeSpecBuilder = TypeSpec.classBuilder(beanName)
				.addSuperinterface(ParameterizedTypeName.get(IObserverConvertor::class.asClassName(), rawClassName))
				.addModifiers(KModifier.DATA)

			// 主构造函数
			val primaryConstructorBuilder = FunSpec.constructorBuilder()

			// 内部私有的 Observer 对象
			val innerObserverBuilder = PropertySpec.varBuilder("_innerObserver", rawClassName.asNullable(), KModifier.PRIVATE)
				.addAnnotation(Transient::class.java)
				.initializer("null")
			typeSpecBuilder.addProperty(innerObserverBuilder.build())

			// 内部单例构造器
			val companionObjBuilder = TypeSpec.companionObjectBuilder()

			// 从观察对象转换的方法
			val fromFuncBuilder = FunSpec.builder("from")
				.addModifiers(KModifier.PUBLIC)
				.returns(beanClassName)

			fromFuncBuilder.addParameter("observer", rawClassName)
			fromFuncBuilder.addStatement("val bean = %T()", beanClassName)

			// 转换为观察对象的方法
			val asFuncBuilder = FunSpec.builder("asObserver")
				.addModifiers(KModifier.PUBLIC)
				.addModifiers(KModifier.OVERRIDE)
				.returns(rawClassName)

			asFuncBuilder.addStatement("if(this._innerObserver != null) return this._innerObserver!!")
			asFuncBuilder.addStatement("val observer = %T()", rawClassName)
			asFuncBuilder.addStatement("this._innerObserver = observer")

			// 分配观察主体方法
			val assignFuncBuilder = FunSpec.builder("assignSubject")
				.addModifiers(KModifier.PUBLIC)
				.receiver(rawClassName)

			// 附着观察主体方法
			val attachFuncBuilder = FunSpec.builder("attachSubject")
				.addModifiers(KModifier.PUBLIC)
				.receiver(rawClassName)

			// 分离观察主体方法
			val detachFuncBuilder = FunSpec.builder("detachSubject")
				.addModifiers(KModifier.PUBLIC)
				.receiver(rawClassName)

			// 模型类扩展方法
			val extensionFuncBuilder = FunSpec.builder("asBean")
				.addModifiers(KModifier.PUBLIC)
				.receiver(rawClassName)
				.returns(beanClassName)

			extensionFuncBuilder.addStatement("return %T.from(this)", beanClassName)


			fieldList.forEach {
				var subjectKey: String? = null
				val anno = it.getAnnotation(ObserverKey::class.java)
				if(anno != null) {
					subjectKey = anno.key
				}
				val typeName = it.asType().asTypeName()
				val fieldName = it.simpleName.toString()
				var isObserver = false
				val realTypeName = if(typeName.toString().startsWith(OBSERVER_CLASS_NAME)) {
					isObserver = true
					javaTypeToKotlinType((typeName as ParameterizedTypeName).typeArguments[0])
				} else {
					javaTypeToKotlinType(typeName)
				}

				val defaultValue = typeDefaultValue(realTypeName)

				val parameterBuilder = ParameterSpec.builder(fieldName, realTypeName, KModifier.PUBLIC)
					.defaultValue(defaultValue)
				primaryConstructorBuilder.addParameter(parameterBuilder.build())
				val propertySpecBuilder =
					PropertySpec.varBuilder(fieldName, realTypeName, KModifier.PUBLIC)
						.initializer(fieldName)

				if(isObserver) {
					if(subjectKey != null) {
						val key = if(subjectKey.startsWith("#")) {
							"this.${subjectKey.substring(1)}.toString()"
						} else {
							"\"$subjectKey\""
						}
						assignFuncBuilder.addStatement("SubjectPool.observe(this.$fieldName, $key, true)")
						attachFuncBuilder.addStatement("this.$fieldName.attachSubject()")
						detachFuncBuilder.addStatement("this.$fieldName.detachSubject()")
					}
					fromFuncBuilder.addStatement("bean.$fieldName = observer.$fieldName.value ?: $defaultValue")
					asFuncBuilder.addStatement("observer.$fieldName.value = this.$fieldName")
				} else {
					fromFuncBuilder.addStatement("bean.$fieldName = observer.$fieldName ?: $defaultValue")
					asFuncBuilder.addStatement("observer.$fieldName = this.$fieldName")
				}

				typeSpecBuilder.addProperty(propertySpecBuilder.build())
			}

			// 处理 From 方法返回值
			fromFuncBuilder.addStatement("return bean")

			// 处理 as 方法返回值
			if(initMethods.isNotBlank()) {
				asFuncBuilder.addStatement("observer.$initMethods")
			}
			asFuncBuilder.addStatement("observer.assignSubject()")
			asFuncBuilder.addStatement("return observer")

			companionObjBuilder.addFunction(fromFuncBuilder.build())
			typeSpecBuilder.primaryConstructor(primaryConstructorBuilder.build())
			typeSpecBuilder.addType(companionObjBuilder.build())
			typeSpecBuilder.addFunction(asFuncBuilder.build())

			val fileSpecBuilder = FileSpec.builder(rawClassPackage, beanName)
				.addType(typeSpecBuilder.build())

			fileSpecBuilder.addAliasedImport(poolClassName, "SubjectPool")
			fileSpecBuilder.addComment("// Code is Auto - Generated. Don't modify by manual")
			fileSpecBuilder.addFunction(assignFuncBuilder.build())
			fileSpecBuilder.addFunction(attachFuncBuilder.build())
			fileSpecBuilder.addFunction(detachFuncBuilder.build())
			fileSpecBuilder.build().writeTo(dir2)
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// 代码分割线 on 2021/12/27 7:08 下午 by CimZzz
	// 以下内容为:
	// 内部类
	///////////////////////////////////////////////////////////////////////////

}